package week4th.jsjf;

import week4th.jsjf.exceptions.ElementNotFoundException;
import week4th.jsjf.exceptions.EmptyCollectionException;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;


public abstract class LinkedList<T> implements ListADT<T>, Iterable<T>
{
    protected int count;
    protected LinearNode<T> head, tail;
	protected int modCount;


    public LinkedList()
    {
        count = 0;
        head = tail = null;
		modCount = 0;
	}

    public T removeFirst() throws EmptyCollectionException
    {
        if (isEmpty())
            throw new EmptyCollectionException("LinkedList");
        LinearNode<T> current = head;
        head = head.getNext();
        count--;
        modCount++;
        return current.getElement();
    }

    public T removeLast() throws EmptyCollectionException
    {
        if (isEmpty())
            throw new EmptyCollectionException("LinkedList");
        LinearNode<T> current = head;
        while(current.getNext()!=tail)
            current = current.getNext();
        tail = current;
        count --;
        modCount ++;
        return current.getNext().getElement();
    }

    public T remove(T targetElement) throws EmptyCollectionException,
            ElementNotFoundException
    {
        if (isEmpty())
            throw new EmptyCollectionException("LinkedList");

        boolean found = false;
        LinearNode<T> previous = null;
        LinearNode<T> current = head;

        while (current != null && !found)
            if (targetElement.equals(current.getElement()))
                found = true;
            else
            {
                previous = current;
                current = current.getNext();
            }

        if (!found)
            throw new ElementNotFoundException("LinkedList");

        if (size() == 1)
            head = tail = null;
        else if (current.equals(head))
            head = current.getNext();
        else if (current.equals(tail))
        {
            tail = previous;
            tail.setNext(null);
        }
        else
            previous.setNext(current.getNext());

        count--;
		modCount++;

        return current.getElement();
    }

    public T first() throws EmptyCollectionException
    {
        T result;
        if (isEmpty())
            throw  new EmptyCollectionException("LinkedList");
        else
            result = head.getElement();
        return result;
    }

    public T last() throws EmptyCollectionException
    {
        return tail.getElement();
    }

    public boolean contains(T targetElement) throws
            EmptyCollectionException
    {
        boolean found = false;
        if (isEmpty())
            throw new EmptyCollectionException("LindedList");
        else {
            T result;
            if (count == 1) {
                if (targetElement == head.getElement())
                    found = true;
                else
                    found = false;
            }
            else {
                LinearNode<T> previous = head;
                LinearNode<T> current = head.getNext();
                if (previous.getElement() == targetElement)
                    found = true;
                else {
                    while (current.getElement() != targetElement && current.getNext() != null)
                        current = current.getNext();
                    if (current.getElement() == targetElement)
                        found = true;
                }
            }
        }
        return found;
    }

    public boolean isEmpty()
    {
        if (count==0)
            return true;
        else
            return false;
    }

    public int size()
    {
        return count;
    }

    public String toString()
    {
        String  result="";
        LinearNode<T> current = head;
        for (int i = 0; i < count; i++) {
            result += current.getElement() + " ";
            current = current.getNext();
        }
        return  result;
    }

    public Iterator<T> iterator()
    {
        return new LinkedListIterator();
    }

	private class LinkedListIterator implements Iterator<T>
	{
		private int iteratorModCount;
		private LinearNode<T> current;

		public LinkedListIterator()
		{
			current = head;
			iteratorModCount = modCount;
		}

		public boolean hasNext() throws ConcurrentModificationException
		{
			if (iteratorModCount != modCount) 
				throw new ConcurrentModificationException();
			
			return (current != null);
		}

		public T next() throws ConcurrentModificationException
		{
			if (!hasNext())
				throw new NoSuchElementException();
			
			T result = current.getElement();
			current = current.getNext();
			return result;
		}

		public void remove() throws UnsupportedOperationException
		{
			throw new UnsupportedOperationException();
		}
	}
	
}


